package good.idea.data.utils;



import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.util.*;

/**
 * 反射工具类。
 */

@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class RefUtils {

    public static final String CGLIB_CLASS_SEPARATOR = "$$";

    public static Object ConvertObj(String propty, String value, Class<?> proptyType) {

        if (proptyType.equals(String.class)) {
            return value;
        } else if (proptyType.equals(Integer.class)) {
            double val = Double.parseDouble(String.valueOf(value));
            Integer valInt = (int) val;
            return valInt;
        } else if (proptyType.equals(Double.class)) {
            if (value.indexOf("(") > -1 && value.indexOf(")") > -1) {
                value = value.replace("(", "");
                value = "-" + value.replace(")", "");
            }
            value = value.replace(",", "");
            return Double.parseDouble(String.valueOf(value));
        } else if (proptyType.equals(Long.class)) {
            return Long.parseLong(String.valueOf(value));
        } else if (proptyType.equals(Date.class)) {
            Date date = null;
            if ("NOW".equals(value)) {
                date = new Date();
            } else {
                date = LangUtils.parseDate(value);
            }
            return date;
        } else if (proptyType.equals(Short.class)) {
            return Short.parseShort(String.valueOf(value));
        } else if (proptyType.equals(Byte.class)) {
            return null;
        } else {

        }
        return proptyType;
    }


    public static void invokeAddMethod(Object obj, String fieldName, Object value) throws Exception {
        String methodName = "add" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        ;
        invokeMethod(obj, methodName, new Class[]{value.getClass()}, new Object[]{value});
    }

    /**
     * 调用Getter方法.
     */
    public static Object invokeGetterMethod(Object obj, String propertyName) {
        String getterMethodName = "get" + StringUtils.capitalize(propertyName);
        return invokeMethod(obj, getterMethodName, new Class[]{}, new Object[]{});
    }

    /**
     * 调用Setter方法.使用value的Class来查找Setter方法.
     */
    public static void invokeSetterMethod(Object obj, String propertyName, Object value) {
        invokeSetterMethod(obj, propertyName, value, null);
    }

    /**
     * 调用Setter方法.
     *
     * @param propertyType 用于查找Setter方法,为空时使用value的Class替代.
     */
    public static void invokeSetterMethod(Object obj, String propertyName, Object value, Class<?> propertyType) {
        Class<?> type = propertyType != null ? propertyType : value.getClass();
        String setterMethodName = "set" + StringUtils.capitalize(propertyName);
        invokeMethod(obj, setterMethodName, new Class[]{type}, new Object[]{value});
    }

    /**
     * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
     * （修改：支持级联属性）
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        if (obj == null || fieldName == null) {
            return null;
        }
        Object result = obj;
        while (true) {
            int i = fieldName.indexOf(".");
            String parentName = (i > -1 ? fieldName.substring(0, i) : fieldName);
            Field field = getAccessibleField(result, parentName);
            if (field == null) {
                break;
            }
            try {
                result = field.get(result);
            } catch (IllegalAccessException e) {
                break;
            }
            if (i > -1) {
                fieldName = fieldName.substring(i + 1);
            } else {
                break;
            }
        }
        return result;
    }

    /**
     * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
     * （修改：支持级联属性）
     */
    public static void setFieldValue(Object obj, String fieldName, Object value) {
        if (obj == null || fieldName == null) return;
        Object o = obj;
        int i = fieldName.lastIndexOf('.');
        if (i > -1) {
            o = getFieldValue(o, fieldName.substring(0, i));
            fieldName = fieldName.substring(i + 1);
        }
        Field field = getAccessibleField(o, fieldName);
        if (field == null) {
            return;
        }

        try {
            field.set(o, value);
        } catch (IllegalAccessException e) {
            //LOG.error("不可能抛出的异常:{}", e.getMessage());
        }
    }

    /**
     * 直接设置对象属性值
     * 基本类型、部分引用类型支持（如：date）
     */
    public static void setFieldValueAuto(Object obj, String fieldName, Object value) {
        if (obj == null || fieldName == null) return;
        Object o = obj;
        int i = fieldName.lastIndexOf('.');
        if (i > -1) {
            o = getFieldValue(o, fieldName.substring(0, i));
            fieldName = fieldName.substring(i + 1);
        }
        Field field = getAccessibleField(o, fieldName);
        if (field == null) {
            return;
        } else {
            try {
                Class s = value.getClass();
                if (field.getType().equals(Object.class)) {
                    field.set(o, value);
                } else if (field.getClass().equals(value.getClass())) {
                    field.set(o, value);
                } else {
                    if (value != null) {
                        String strval = String.valueOf(value);
                        if (value instanceof Double) {
                            if (strval.endsWith(".0")) {
                                strval = strval.replace(".0", "");
                            }
                        }
                        Object ob = RefUtils.ConvertObj(field.getName(), strval, field.getType());
                        field.set(o, ob);
                    }

                }
            } catch (IllegalAccessException e) {
                //LOG.error("不可能抛出的异常:{}", e.getMessage());
            }
        }

    }

    /**
     * 循环向上转型, 获取对象的DeclaredField,	 并强制设置为可访问.
     * <p>
     * 如向上转型到Object仍无法找到, 返回null.
     */
    public static Field getAccessibleField(final Object obj, final String fieldName) {
        //Assert.notNull(obj, "object不能为空");
        //Assert.hasText(fieldName, "fieldName");
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                Field field = superClass.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {//NOSONAR
                // Field不在当前类定义,继续向上转型
            }
        }
        return null;
    }

    public static Field[] getDeclaredFields(final Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        return fields;
    }

    /**
     * 对于被cglib AOP过的对象, 取得真实的Class类型.
     */
    public static Class<?> getUserClass(Class<?> clazz) {
        if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !Object.class.equals(superClass)) {
                return superClass;
            }
        }
        return clazz;
    }

    /**
     * 直接调用对象方法, 无视private/protected修饰符.
     * 用于一次性调用的情况.
     */
    public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
                                      final Object[] args) {
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
        }

        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw convertReflectionExceptionToUnchecked(e);
        }
    }


    /**
     * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
     * 如向上转型到Object仍无法找到, 返回null.
     * <p>
     * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
     */
    public static Method getAccessibleMethod(final Object obj, final String methodName,
                                             final Class<?>... parameterTypes) {
        //Assert.notNull(obj, "object不能为空");

        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                Method method = superClass.getDeclaredMethod(methodName, parameterTypes);

                method.setAccessible(true);

                return method;

            } catch (NoSuchMethodException e) {//NOSONAR
                // Method不在当前类定义,继续向上转型
            }
        }
        return null;
    }

    /**
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
     * 如无法找到, 返回Object.class.
     * eg.
     * public UserDao extends HibernateDao<User>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or Object.class if cannot be determined
     */
    public static <T> Class<T> getSuperClassGenricType(final Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
     * 如无法找到, 返回Object.class.
     * <p>
     * 如public UserDao extends HibernateDao<User,Long>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     * @return the index generic declaration, or Object.class if cannot be determined
     */
    public static Class getSuperClassGenricType(final Class clazz, final int index) {

        Type genType = clazz.getGenericSuperclass();

        if (!(genType instanceof ParameterizedType)) {
            //LOG.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }

        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

        if (index >= params.length || index < 0) {
            //LOG.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
            //	+ params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            //	LOG.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }

        return (Class) params[index];
    }

    /**
     * 将反射时的checked exception转换为unchecked exception.
     */
    public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
        if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
                || e instanceof NoSuchMethodException) {
            return new IllegalArgumentException("Reflection Exception.", e);
        } else if (e instanceof InvocationTargetException) {
            return new RuntimeException("Reflection Exception.", ((InvocationTargetException) e).getTargetException());
        } else if (e instanceof RuntimeException) {
            return (RuntimeException) e;
        }
        return new RuntimeException("Unexpected Checked Exception.", e);
    }


    /**
     * 对象属性值的复制，自动忽略不存在以及类型不一致的属性
     *
     * @param soure  数据源
     * @param target 目标对象
     * @throws Exception
     * @author chenhuanmou
     */
    public static void copyProperties(Object soure, Object target) {
        copyProperties(soure, target, null, null);
    }

    public static void copyProperties(Object soure, Object target, String encodR, String encodT) {
        try {
            Field[] soureFields = soure.getClass().getDeclaredFields();
            Field[] targetFields = target.getClass().getDeclaredFields();
            for (int i = 0; i < soureFields.length; i++) {
                Field sourField = soureFields[i];
                String sourFieldName = sourField.getName();
                for (int j = 0; j < targetFields.length; j++) {
                    Field targetField = targetFields[j];
                    if (targetField.getName().equals(sourFieldName) && targetField.getType().equals(sourField.getType())) {
                        sourField.setAccessible(true);
                        targetField.setAccessible(true);
                        Object valObj = sourField.get(soure);
                        if (encodR != null && valObj != null && sourField.getType().equals(String.class)) {
                            String str = new String(valObj.toString().getBytes(encodR), encodT);
                            valObj = str;
                        }
                        targetField.set(target, valObj);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void copyPropertiesNotNull(Object soure, Object target, int model) {
        try {
            Field[] soureFields = soure.getClass().getDeclaredFields();
            Field[] targetFields = target.getClass().getDeclaredFields();

            for (int i = 0; i < soureFields.length; i++) {
                Field sourField = soureFields[i];
                String sourFieldName = sourField.getName();
                for (int j = 0; j < targetFields.length; j++) {
                    Field targetField = targetFields[j];
                    if (targetField.getModifiers() != 25 && targetField.getModifiers() != 26) {
                        if (targetField.getName().equals(sourFieldName) && targetField.getType().equals(sourField.getType())) {
                            sourField.setAccessible(true);
                            targetField.setAccessible(true);
                            Object val = sourField.get(soure);
                            if (model == 1) {
                                if (val != null) {
                                    targetField.set(target, val);
                                }
                            } else if (model == 2) {
                                if (targetField.get(target) == null) {
                                    targetField.set(target, val);
                                }
                            }
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据制定的属性复制值
     *
     * @param soure
     * @param target
     * @param fields
     * @throws Exception
     */
    public static void copyProperties(Object soure, Object target, String fields, boolean notNull) {
        try {
            if (fields != null) {
                Field[] soureFields = soure.getClass().getDeclaredFields();
                Field[] targetFields0 = target.getClass().getDeclaredFields();
                String[] fs = fields.split(",");
                Field[] targetFields = new Field[fs.length];

                for (int i = 0; i < fs.length; i++) {
                    String f = fs[i];
                    for (int j = 0; j < targetFields0.length; j++) {
                        Field fi = targetFields0[j];
                        if (fi.getName().equalsIgnoreCase(f)) {
                            targetFields[i] = fi;
                            break;
                        }
                    }
                }
                for (int i = 0; i < soureFields.length; i++) {
                    Field sourField = soureFields[i];
                    String sourFieldName = sourField.getName();
                    for (int j = 0; j < targetFields.length; j++) {
                        Field targetField = targetFields[j];
                        if (targetField.getName().equals(sourFieldName) && targetField.getType().equals(sourField.getType())) {
                            sourField.setAccessible(true);
                            targetField.setAccessible(true);
                            Object val = sourField.get(soure);
                            if (val != null || !notNull) {
                                targetField.set(target, sourField.get(soure));
                            }
                            break;
                        }
                    }
                }
            }
        } catch (SecurityException e) {
            //e.printStackTrace();
        } catch (IllegalArgumentException e) {
            //e.printStackTrace();
        } catch (IllegalAccessException e) {
            //e.printStackTrace();
        }
    }

    /**
     * map 转换为对象
     *
     * @param soure List<?> or Object
     * @param clas  class
     * @throws Exception
     */
    public static Object coverMapToEntry(Object soure, Class<?> clas) throws Exception {
        if (soure != null) {
            Field[] fields = clas.getDeclaredFields();
            if (soure instanceof List) {
                List<Object> lists = new ArrayList<Object>();
                List<Map<String, Object>> items = (List<Map<String, Object>>) soure;
                for (int i = 0; i < items.size(); i++) {
                    Map<String, Object> map = items.get(i);
                    Object obj = clas.newInstance();
                    RefUtils.coverMapToObjectVal(map, obj, fields);
                    lists.add(obj);
                }
                List<Object> soureItems = (List<Object>) soure;
                soureItems.clear();
                for (int i = 0; i < lists.size(); i++) {
                    soureItems.add(lists.get(i));
                }
            } else if (soure instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) soure;
                RefUtils.coverMapToObjectVal(map, soure, fields);
            }
        }
        return soure;
    }

    private static void coverMapToObjectVal(Map<String, Object> map, Object it, Field[] targetFields) {
        try {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey().replace("_", "");
                Object value = entry.getValue();
                if (value != null) {
                    for (int j = 0; j < targetFields.length; j++) {
                        Field targetField = targetFields[j];
                        if (key.equalsIgnoreCase(targetField.getName())) {
                            if (value.getClass().equals(BigDecimal.class)) {
                                value = String.valueOf(value);
                            }
                            RefUtils.setFieldValueAuto(it, targetField.getName(), value);
                        }
                    }

                }

            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
